package cn.dansj.controller;

import cn.dansj.common.request.annotation.http.GetMethod;
import cn.dansj.common.request.annotation.http.PostMethod;
import cn.dansj.common.request.annotation.http.RequestMappingMethod;
import cn.dansj.common.request.annotation.http.RequestParam;
import cn.dansj.common.utils.json.DictMap;
import cn.dansj.entity.InterfaceGroup;
import cn.dansj.common.utils.enums.BooleanType;
import cn.dansj.enums.GlobalEnv;
import cn.dansj.common.utils.enums.ResponseCode;
import cn.dansj.service.InterfaceGroupService;
import cn.dansj.common.utils.transfer.*;
import cn.dansj.common.utils.transfer.Md5Utils;
import cn.dansj.utils.project.ProjectUtils;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import cn.dansj.entity.InterfaceHistory;
import cn.dansj.entity.InterfaceInfo;
import cn.dansj.entity.RequestLog;
import cn.dansj.common.utils.bean.Return;
import cn.dansj.service.InterfaceHistoryService;
import cn.dansj.service.InterfaceInfoService;
import cn.dansj.service.RequestLogService;
import cn.dansj.common.utils.http.UrlUtils;
import cn.dansj.utils.interfaces.InterfaceUtils;
import cn.dansj.common.redis.RedisUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

@RequestMappingMethod("/interface")
public class InterfaceController {
    private final InterfaceInfoService interfaceInfoService;
    private final InterfaceGroupService interfaceGroupService;
    private final InterfaceHistoryService interfaceHistoryService;
    private final Logger logger = LoggerFactory.getLogger(InterfaceController.class);
    private final RedisUtils redisUtils;
    private final RequestLogService requestLogService;

    public InterfaceController(RedisUtils redisUtils, InterfaceInfoService interfaceInfoService, InterfaceGroupService interfaceGroupService,
                               InterfaceHistoryService interfaceHistoryService, RequestLogService requestLogService) {
        this.redisUtils = redisUtils;
        this.interfaceInfoService = interfaceInfoService;
        this.interfaceGroupService = interfaceGroupService;
        this.interfaceHistoryService = interfaceHistoryService;
        this.requestLogService = requestLogService;
    }

    @GetMethod("/data")
    public Return<?> getData(@RequestParam("search") JSONObject search, @RequestParam int page, @RequestParam int limit) {
        String name = search.getString("name");
        String url = search.getString("url");
        String status = search.getString("status");
        String method = search.getString("method");
        String type = search.getString("type");
        String apiType = search.getString("apiType");
        String group = search.getString("group");
        Page<InterfaceInfo> interfaceRequestPage = interfaceInfoService.searchInterface(page, limit, name, url, status, method, type, apiType, group);
        return Return.T(interfaceRequestPage.getContent()).setTotalRecord(interfaceRequestPage.getTotalElements());
    }

    @GetMethod("/detail")
    public Return<?> getDetail(@RequestParam(required = false) String sequence, @RequestParam(required = false) boolean forDoc) {
        InterfaceInfo interfaceInfo = null;
        if (Verification.checkNotNull(sequence)) {
            interfaceInfo = interfaceInfoService.getBySequence(sequence);
            if (interfaceInfo == null) {
                return Return.T(false, ResponseCode.NotExist.getError());
            }

            if (forDoc) {
                interfaceInfo.setUrl(UrlUtils.formatUrl(interfaceInfo.getUrl()));
                InterfaceUtils.parseRefSql(interfaceInfo, interfaceInfoService);
            } else {
                return Return.T(interfaceInfo);
            }
        }
        return Return.T(interfaceInfo);
    }

    @GetMethod("/group")
    public Return<?> getGroup() {
        return Return.T(interfaceGroupService.getGround());
    }

    @GetMethod(value = "/authList")
    public Return<?> getAuthList() {
        return Return.T(interfaceInfoService.getAuthList());
    }

    @GetMethod("/history")
    public Return<?> getHistory(@RequestParam(required = false) String sequence) {
        List<InterfaceHistory> history = interfaceHistoryService.getHistory(sequence);
        return Return.T(history);
    }

    @PostMethod("/save")
    public Return<?> save(@RequestParam JSONObject request) {
        InterfaceInfo interfaceInfo;
        String sequence = request.getString("sequence");
        String code = request.getString("code");
        JSONArray params = request.getJSONArray("params");
        String interfaceName = request.getString("name");
        String comments = request.getString("comments");
        String method = request.getString("method");
        String returnType = request.getString("returnType");
        String type = request.getString("type");
        String response = request.getString("response");
        String group = request.getString("group");
        String blackList = request.getString("blackList");
        String whiteList = request.getString("whiteList");
        int apiType = request.getIntValue("apiType");
        int status = Transformation.nvl(request.getIntValue("status"), 1);
        int checkPermission = request.getIntValue("checkPermission");
        int requestLock = request.getIntValue("requestLock");
        int encrypt = request.getIntValue("encrypt");
        int useCache = Transformation.nvl(request.getIntValue("useCache"), 0);
        String url = UrlUtils.formatUrl(request.getString("url"));

        boolean urlExists = interfaceInfoService.checkInterfaceUrl(Transformation.nvl(sequence, "-999"), url);
        if (urlExists) {
            return Return.T(false, String.format("接口地址[%s]已存在, 请检查", url));
        }

        boolean nameExists = interfaceInfoService.checkInterfaceName(Transformation.nvl(sequence, "-999"), interfaceName);
        if (nameExists) {
            return Return.T(false, String.format("接口名称[%s]已存在, 请检查", interfaceName));
        }

        Timestamp now = GetTime.dbTimestamp();

        if (Verification.checkNotNull(sequence)) {
            interfaceInfo = interfaceInfoService.getBySequence(sequence);
        } else {
            sequence = RandomUtils.sequence();
            interfaceInfo = new InterfaceInfo();
            interfaceInfo.setSequence(sequence);
            interfaceInfo.setCrtTime(now);
        }

        interfaceInfo.setStatus(status);
        interfaceInfo.setResponse(response);
        interfaceInfo.setInterfaceName(interfaceName);
        interfaceInfo.setUrl(url);
        interfaceInfo.setCode(code);
        interfaceInfo.setComments(comments);
        interfaceInfo.setMethod(method);
        interfaceInfo.setType(type);
        interfaceInfo.setApiType(apiType);
        interfaceInfo.setReturnType(returnType);
        interfaceInfo.setCheckPermission(checkPermission);
        interfaceInfo.setRequestLock(requestLock);
        interfaceInfo.setInGroup(group);
        interfaceInfo.setEncrypt(encrypt);
        interfaceInfo.setUseCache(useCache);
        interfaceInfo.setWhiteList(whiteList);
        interfaceInfo.setBlackList(blackList);
        JSONObject p = new JSONObject();
        for (Object object : params) {
            String name = ((Map<?, ?>) object).get("name").toString();
            p.put(name, object);
        }
        interfaceInfo.setParams(p.toJSONString());
        interfaceInfo.setUpdateTime(now);

        // 保存接口信息
        interfaceInfoService.save(interfaceInfo);

        // 删除分组标签，重新保存分组标签
        interfaceGroupService.deleteByInterfaceSequence(sequence);
        List<InterfaceGroup> interfaceGroups = new ArrayList<>();
        for (String sg : StringUtils.split(group, ",")) {
            final InterfaceGroup interfaceGroup = new InterfaceGroup();
            interfaceGroup.setInterfaceSequence(sequence);
            interfaceGroup.setSequence(RandomUtils.sequence());
            interfaceGroup.setInGroup(sg);
            interfaceGroup.setCreateTime(now);
            interfaceGroups.add(interfaceGroup);
        }
        interfaceGroupService.save(interfaceGroups);

        //缓存接口信息
        cacheInterface(interfaceInfo);

        //保留接口历史记录信息
        String codeMd5 = Md5Utils.md5(interfaceInfo.getCode().replaceAll("\\s+", ""));
        int count = interfaceHistoryService.countInterfaceHistoryByMd5(codeMd5);
        if (count == 0) {
            GlobalEnv.threadPool.execute(() -> {
                try {
                    InterfaceHistory interfaceHistory = new InterfaceHistory();
                    interfaceHistory.setInterfaceName(interfaceName);
                    interfaceHistory.setInterfaceSequence(interfaceInfo.getSequence());
                    interfaceHistory.setSequence(RandomUtils.sequence());
                    interfaceHistory.setMd5(codeMd5);
                    interfaceHistory.setResponse(response);
                    interfaceHistory.setCrtTime(GetTime.dbTimestamp());
                    interfaceHistory.setParams(interfaceInfo.getParams());
                    interfaceHistory.setCode(interfaceInfo.getCode());
                    interfaceHistoryService.save(interfaceHistory);
                } catch (Exception e) {
                    logger.error("接口历史记录保存失败", e);
                }
            });
        }
        ProjectUtils.backUpInterfaceInfo(interfaceInfo);

        return Return.T(sequence);
    }

    @GetMethod("/key")
    public Return<?> getInterfaceKey() {
        return Return.T(InterfaceUtils.env.keySet());
    }

    @GetMethod(value = "/log", nologging = BooleanType.TRUE)
    public Return<?> getLog(@RequestParam("search") JSONObject search, @RequestParam int page, @RequestParam int limit) {
        String url = search.getString("url");
        int status = Verification.checkNotNull(search.getString("status")) ? search.getIntValue("status") : -1;
        long cost = search.getLongValue("cost");
        String date = search.getString("date");
        final int apiType = Transformation.parseInt(search.getString("apiType"), -1);
        Page<RequestLog> requestLogs = requestLogService.search(page, limit, url, status, cost, date, InterfaceRequestController.class.getName(), "", apiType);
        return Return.T(requestLogs.getContent()).setTotalRecord(requestLogs.getTotalElements());
    }

    @PostMethod("/cache")
    public Return<?> cache() {
        redisUtils.deleteByPrefix("Interface@*@*");
        final List<InterfaceInfo> allInterface = interfaceInfoService.getAllInterface();
        for (InterfaceInfo interfaceInfo : allInterface) {
            cacheInterface(interfaceInfo);
        }
        return Return.T(allInterface.size());
    }

    @PostMethod("/delete")
    public Return<?> delete(@RequestParam String sequence) {
        final InterfaceInfo interfaceInfo = interfaceInfoService.getBySequence(sequence);
        final int db = interfaceInfoService.deleteBySequence(sequence);
        final int cache = deleteInterface(interfaceInfo);
        return Return.T(DictMap.of("db", db, "cache", cache));
    }

    private int deleteInterface(InterfaceInfo interfaceInfo) {
        //按sequence删除修改了名称的,在按名称删除历史接口不存在的,保证按名称和sequence都只有一条记录
        List<String> keys = redisUtils.keys(String.format("Interface@*@%s", interfaceInfo.getSequence()));
        List<String> byName = redisUtils.keys(String.format("Interface@%s@*", Md5Utils.md5(interfaceInfo.getUrl() + interfaceInfo.getMethod())));
        keys.addAll(byName);

        final List<String> keySet = redisUtils.keys("InterfaceResult@*");
        for (String key : keySet) {
            final Object url = redisUtils.hget(key, "url");
            if (interfaceInfo.getUrl().equals(url)) {
                keys.add(key);
            }
        }

        for (String key : keys) {
            redisUtils.delete(key);
        }
        return new HashSet<>(keys).size();
    }

    private void cacheInterface(InterfaceInfo interfaceInfo) {
        deleteInterface(interfaceInfo);
        JSONObject jsonObject = interfaceInfo.toJSONObject();
        String md5 = String.format("Interface@%s@%s", Md5Utils.md5(interfaceInfo.getUrl() + interfaceInfo.getMethod()), interfaceInfo.getSequence());
        redisUtils.hmset(md5, jsonObject);
    }
}
